/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.anjiplus.template.gaea.generator.service.impl;

import com.anjiplus.template.gaea.generator.domain.Column;
import com.anjiplus.template.gaea.generator.domain.GenConfig;
import com.anjiplus.template.gaea.generator.domain.vo.TableInfo;
import com.anjiplus.template.gaea.generator.exception.BadRequestException;
import com.anjiplus.template.gaea.generator.repository.ColumnInfoRepository;
import com.anjiplus.template.gaea.generator.service.GeneratorService;
import com.anjiplus.template.gaea.generator.service.GeneratorUtilService;
import com.anjiplus.template.gaea.generator.utils.FileUtil;
import com.anjiplus.template.gaea.generator.utils.PageUtil;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ZipUtil;

/**
 * @author Zheng Jie
 * @date 2019-01-02
 */
@Service
@RefreshScope
@ConditionalOnExpression("${generator.enabled:false}")
@Primary
public class GeneratorServiceImpl implements GeneratorService {
    private static final Logger log = LoggerFactory.getLogger(GeneratorServiceImpl.class);

    @Autowired
    JdbcTemplate baseJdbcTemplate;

    @Autowired
    private ColumnInfoRepository columnInfoRepository;

    @Autowired
    private GeneratorUtilService genUtil;

    @Value("${generator.tableExclude.mysql:''}")
    private String excludeTables = "";

    @Override
    public Object getTables() {
        // 使用预编译防止sql注入
        String sql = "select table_name ,create_time , engine, "
                + "table_collation, table_comment from information_schema.tables " +
                "where table_schema = (select database()) " +
                "order by create_time desc";
        List<Map<String,Object>> result = baseJdbcTemplate.queryForList(sql);
        List<TableInfo> tableInfos = new ArrayList<>();
        for (Map<String,Object> obj : result) {
            tableInfos.add(new TableInfo(obj.get("table_name"),obj.get("create_time"),
                    obj.get("engine"),obj.get("table_collation"),
                    ObjectUtil.isNotEmpty(obj.get("table_comment")) ?obj.get("table_comment") : "-"));
        }
        return tableInfos;
    }

    @Override
    public Object getTables(String name, int[] startEnd) {
        // 使用预编译防止sql注入
        String tableQry = StringUtils.isNotBlank(name) ? ("'%" + name + "%' ") : "'%%' ";
        String sql = "select table_name ,create_time , engine, "
                + " table_collation, table_comment from information_schema.tables " +
                " where table_schema = (select database()) " +
                " and table_name like " + tableQry + excludeTables+" order by create_time desc";
        sql = sql.concat(" limit "+startEnd[0]+","+startEnd[1]);
        List<Map<String,Object>> result = baseJdbcTemplate.queryForList(sql);
        List<TableInfo> tableInfos = new ArrayList<>();
        for (Map<String,Object> obj : result) {
            tableInfos.add(new TableInfo(obj.get("table_name"),obj.get("create_time"),
                obj.get("engine"),obj.get("table_collation"),
                ObjectUtil.isNotEmpty(obj.get("table_comment")) ?obj.get("table_comment") : "-"));
        }

        String countSql = "SELECT COUNT(*) from information_schema.tables " +
                " where table_schema = (select database()) and table_name like "+tableQry+excludeTables;
        Object totalElements = 0;
        totalElements = baseJdbcTemplate.queryForObject(countSql,Object.class);
        return PageUtil.toPage(tableInfos, totalElements);
    }

    @Override
    public List<Column> getColumns(String tableName) {
        Map m = new HashMap();
        m.put("table_name",tableName);
        List<Column> columnInfos = columnInfoRepository.selectByMap(m);
        if (CollectionUtil.isNotEmpty(columnInfos)) {
            return columnInfos;
        } else {
            columnInfos = queryColumns(tableName);
            columnInfos.stream().forEach(item->columnInfoRepository.updateById(item));
            return columnInfos;
        }
    }

    @Override
    public List<Column> queryColumns(String tableName) {
        // 使用预编译防止sql注入
        String sql = "select column_name, is_nullable, data_type, "
                + "column_comment, column_key, extra,character_maximum_length as maxLength," +
                " column_default as defaultValue" +
                " from information_schema.columns " +
                "where table_name = ? and table_schema = (select database()) order by ordinal_position";
        List<Map<String,Object>> result = baseJdbcTemplate.queryForList(sql,tableName);
        List<Column> columnInfos = new ArrayList<>();
        Column c = null;
        for (Map<String,Object> arr : result) {
            c = new Column(tableName,
                    arr.get("column_name").toString(),"NO".equals(arr.get("is_nullable")),
                    arr.get("data_type").toString(),
                    ObjectUtil.isNotNull(arr.get("column_comment")) ? arr.get("column_comment").toString(): null,
                    ObjectUtil.isNotNull(arr.get("column_key")) ? arr.get("column_key").toString() : null,
                    ObjectUtil.isNotNull(arr.get("extra")) ? arr.get("extra").toString() : null);
            if(arr.get("maxLength")!=null) {
                c.setMaxLength("" + arr.get("maxLength"));
            }
            if(arr.get("defaultValue")!=null && "String,Integer".indexOf(c.getColumnType())>=0) {
                c.setDefaultValue(arr.get("defaultValue"));
            }
            columnInfos.add(c);
        }
        return columnInfos;
    }

    @Override
    public void sync(List<Column> columnInfos, List<Column> columnInfoList) {
        // 第一种情况，数据库类字段改变或者新增字段
        for (Column col : columnInfoList) {
            // 根据字段名称查找
            List<Column> columns = columnInfos.stream()
                    .filter(c -> c.getColumnName().equals(col.getColumnName()))
                    .collect(Collectors.toList());
            // 如果能找到，就修改部分可能被改字段
            if (CollectionUtil.isNotEmpty(columns)) {
                Column column = columns.get(0);
                column.setColumnType(col.getColumnType());
                column.setExtra(col.getExtra());
                column.setKeyType(col.getKeyType());
                if (StringUtils.isBlank(column.getRemark())) {
                    column.setRemark(col.getRemark());
                }
                columnInfoRepository.updateById(column);
            } else {
                // 如果找不到，则保存新字段信息
                columnInfoRepository.insert(col);
            }
        }
        // 第二种情况，数据库字段删除了
        for (Column col : columnInfos) {
            // 根据字段名称查找
            List<Column> columns = columnInfoList.stream()
                    .filter(c -> c.getColumnName().equals(col.getColumnName()))
                    .collect(Collectors.toList());
            // 如果找不到，就代表字段被删除了，则需要删除该字段
            if (CollectionUtil.isEmpty(columns)) {
                columnInfoRepository.deleteBatchIds(columns.stream()
                        .map(i->i.getId()).collect(Collectors.toList()));
            }
        }
    }

    @Override
    public void saveColumnGenConfig(List<Column> cols) {
        if(cols.get(0).getId()==null){
            columnInfoRepository.insertBatch(cols);
            return;
        }
        cols.stream().forEach(col->{
            columnInfoRepository.updateById(col);
        });
    }

    @Override
    public void generator(GenConfig genConfig, List<Column> columns) {
        if (genConfig.getId() == null) {
            throw new BadRequestException("请先配置生成器");
        }
        try {
            genUtil.generatorCode(columns, genConfig);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BadRequestException("生成失败，请手动处理已生成的文件");
        }
    }

    @Override
    public ResponseEntity<Object> preview(GenConfig genConfig, List<Column> columns) {
        if (genConfig.getId() == null) {
            throw new BadRequestException("请先配置生成器");
        }
        List<Map<String, Object>> genList = genUtil.preview(columns, genConfig);
        return new ResponseEntity<>(genList, HttpStatus.OK);
    }

    @Override
    public void download(GenConfig genConfig, List<Column> columns,
                         HttpServletRequest request, HttpServletResponse response) {
        if (genConfig.getId() == null) {
            throw new BadRequestException("请先配置生成器");
        }
        try {
            File file = new File(genUtil.download(columns, genConfig));
            String zipPath = file.getPath() + ".zip";
            ZipUtil.zip(file.getPath(), zipPath);
            FileUtil.downloadFile(request, response, new File(zipPath), true);
        } catch (IOException e) {
            throw new BadRequestException("打包失败");
        }
    }
}
